/**
 * led8s595.c
 *
 * @author PIANO
 *
 */
#include "led8s595.h"

/**
 * 共阴和共阳切换只需要在 ledOut DIO(x)处更改
 *
 */

/**
 * globals
 */
// 数字真值表（共阳）
const uint8_t truthTableNumber[16] = {
//0-9
		0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90,
		//AbCdEF
		0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e };
// 字母真值表（共阳）
const uint8_t truthTableLetter[26] = {
// 0x41-0x5A		A-Z
// 0x61-0x7A		a-z
		// AbCdFEG
		0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0xC2,
		// HIJKLMn
		0x89, 0xF0, 0xF1, 0x8A, 0xC7, 0xC8, 0xAB,
		// oPqrSt
		0xA3, 0x8C, 0x98, 0xCE, 0xB6, 0x87,
		//UvWXYZ
		0xC1, 0xE3, 0x81, 0x9B, 0x91, 0xA5 };

// 74HC595数码管段控制表
const uint8_t digitTable[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
// 显示BUFFER
uint8_t displayBuffer[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// 闪烁段
int8_t flashSegment = -1;

/**
 * 获取真值（数字）
 *
 * @param
 * @return
 */
uint8_t getTruthNumber(uint8_t number) {
	// 16个数字
	if (number < 16)
		return truthTableNumber[number];
	else
		return 0xBF;
}

/**
 * 获取真值（字母）
 *
 * @param
 * @return
 */
uint8_t getTruthLetter(uint8_t letter) {
	// 0x41-0x5A		A-Z
	// 0x61-0x7A		a-z
	uint8_t index;
	if (letter >= 0x61) {
		index = letter - 0x61;
	} else {
		index = letter - 0x41;
	}
	// 26个字母
	if (index < 26)
		return truthTableLetter[index];
	else
		return 0xBF;
}

/**
 * 解析整数4位
 *
 * @param
 * @return
 */
static void setSegment4Integer(uint8_t *displayBuffer, int16_t value,
		uint16_t decimalDigits) {
	//buffer
	uint8_t buffer[4];
	// 正向超范围
	if (value > 9999) {
		memset(buffer, 0xFE, sizeof buffer);
	}
	//正数
	else if (value >= 0) {

		buffer[0] = getTruthNumber(value / 1000);
		buffer[1] = getTruthNumber(value % 1000 / 100);
		buffer[2] = getTruthNumber(value % 100 / 10);
		buffer[3] = getTruthNumber(value % 10 / 1);
		//小数点
		if (decimalDigits == 3) {
			buffer[0] &= 0x7F;
		} else if (decimalDigits == 2) {
			if (buffer[0] == 0xC0) {
				buffer[0] = 0xFF;
			}
			buffer[1] &= 0x7F;
		} else if (decimalDigits == 1) {
			if (buffer[0] == 0xC0) {
				buffer[0] = 0xFF;
				if (buffer[1] == 0xC0) {
					buffer[1] = 0xFF;
				}
			}
			buffer[2] &= 0x7F;
		} else if (decimalDigits == 0) {
			if (buffer[0] == 0xC0) {
				buffer[0] = 0xFF;
				if (buffer[1] == 0xC0) {
					buffer[1] = 0xFF;
					if (buffer[2] == 0xC0) {
						buffer[2] = 0xFF;
					}
				}
			}
		} else {
			memset(buffer, 0x7F, sizeof buffer);
		}
	}
	// 负数
	else if (value >= -999) {
		// 绝对值
		int16_t value_abs = abs(value);

		buffer[0] = 0xBF;
		buffer[1] = getTruthNumber(value_abs % 1000 / 100);
		buffer[2] = getTruthNumber(value_abs % 100 / 10);
		buffer[3] = getTruthNumber(value_abs % 10 / 1);
		// 小数点
		if (decimalDigits == 2) {
			buffer[1] &= 0x7F;
		} else if (decimalDigits == 1) {
			if (buffer[1] == 0xC0) {
				buffer[1] = 0xFF;
			}
			buffer[2] &= 0x7F;
		} else if (decimalDigits == 0) {
			if (buffer[1] == 0xC0) {
				buffer[1] = 0xFF;
				if (buffer[2] == 0xC0) {
					buffer[2] = 0xFF;
				}
			}
		} else {
			memset(buffer, 0x7F, sizeof buffer);
		}
	}
	// 负向超范围
	else {
		memset(buffer, 0xF7, sizeof buffer);
	}
	//memory copy
	memcpy(displayBuffer, buffer, 4);
}

/**
 * 设定整数显示
 *
 * @param
 * @return
 */
void led8s595SetInteger(int16_t value, uint8_t decimalDigits, uint8_t index) {
	// index: 0 or 1
	if (index > 1)
		return;
	// 设定4位
	setSegment4Integer(&displayBuffer[4 * index], value, decimalDigits);
}

/**
 * 设定长整数显示
 *
 * @param
 * @return
 */
void led8s595SetLong(int32_t value, uint8_t decimalDigits) {
	// 正向超范围
	if (value > 99999999) {
		// 全显示上面横杠
		memset(displayBuffer, 0xFE, sizeof displayBuffer);
		return;
	}
	// 负向超范围
	if (value < -9999999) {
		// 全显示下面横杠
		memset(displayBuffer, 0xF7, sizeof displayBuffer);
		return;
	}
	//buffer
	uint8_t buffer[8];
	//正数
	if (value >= 0) {
		buffer[0] = getTruthNumber(value % 100000000 / 10000000);
		buffer[1] = getTruthNumber(value % 10000000 / 1000000);
		buffer[2] = getTruthNumber(value % 1000000 / 100000);
		buffer[3] = getTruthNumber(value % 100000 / 10000);
		buffer[4] = getTruthNumber(value % 10000 / 1000);
		buffer[5] = getTruthNumber(value % 1000 / 100);
		buffer[6] = getTruthNumber(value % 100 / 10);
		buffer[7] = getTruthNumber(value % 10 / 1);
	}
	// 负数
	else {
		// 绝对值
		int16_t value_abs = abs(value);
		//
		buffer[0] = 0xBF;
		buffer[1] = getTruthNumber(value_abs % 10000000 / 1000000);
		buffer[2] = getTruthNumber(value_abs % 1000000 / 100000);
		buffer[3] = getTruthNumber(value_abs % 100000 / 10000);
		buffer[4] = getTruthNumber(value_abs % 10000 / 1000);
		buffer[5] = getTruthNumber(value_abs % 1000 / 100);
		buffer[6] = getTruthNumber(value_abs % 100 / 10);
		buffer[7] = getTruthNumber(value_abs % 10 / 1);
	}
	// 处理小数点
	if (decimalDigits == 0) {
		// 不做处理
	} else if (decimalDigits > 0 && decimalDigits <= 3) {
		buffer[7 - decimalDigits] &= 0x7F;
	} else {
		// 全是点
		memset(buffer, 0x7F, sizeof buffer);
	}
	// 处理前面的0
	for (int i = 0; i < 8; i++) {
		if (buffer[i] == 0xC0) {
			buffer[i] = 0xFF;
		} else if (buffer[i] == 0xBF) {
			continue;
		} else {
			break;
		}
	}
	//memory copy
	memcpy(displayBuffer, buffer, sizeof buffer);
}

/**
 * 解析浮点数4位
 *
 * @param
 * @return
 */
void setSegment4Float(uint8_t *displayBuffer, float value) {
	//buffer
	uint8_t buffer[4];
	//
	if (value > 9999) {
		buffer[0] = 0x00 & ~0x80;
		buffer[1] = 0x00 & ~0x80;
		buffer[2] = 0x00 & ~0x80;
		buffer[3] = 0x00 & ~0x80;
	} else if (value > 999.9) {
		long l = value * 1;
		buffer[0] = getTruthNumber(l / 1000);
		buffer[1] = getTruthNumber(l % 1000 / 100);
		buffer[2] = getTruthNumber(l % 100 / 10);
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value > 99.99) {
		long l = value * 10;
		buffer[0] = getTruthNumber(l / 1000);
		buffer[1] = getTruthNumber(l % 1000 / 100);
		buffer[2] = getTruthNumber(l % 100 / 10) & ~0x80;
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value > 9.999) {
		long l = value * 100;
		buffer[0] = getTruthNumber(l / 1000);
		buffer[1] = getTruthNumber(l % 1000 / 100) & ~0x80;
		buffer[2] = getTruthNumber(l % 100 / 10);
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value >= 0.0) {
		long l = value * 100;
		buffer[0] = 0xFF;
		buffer[1] = getTruthNumber(l % 1000 / 100) & ~0x80;
		buffer[2] = getTruthNumber(l % 100 / 10);
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value > -9.99) {
		long l = value * -100;
		buffer[0] = 0xBF;
		buffer[1] = getTruthNumber(l % 1000 / 100) & ~0x80;
		buffer[2] = getTruthNumber(l % 100 / 10);
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value > -99.9) {
		long l = value * -10;
		buffer[0] = 0xBF;
		buffer[1] = getTruthNumber(l % 1000 / 100);
		buffer[2] = getTruthNumber(l % 100 / 10) & ~0x80;
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else if (value > -999) {
		long l = value * -1;
		buffer[0] = 0xBF;
		buffer[1] = getTruthNumber(l % 1000 / 100);
		buffer[2] = getTruthNumber(l % 100 / 10);
		buffer[3] = getTruthNumber(l % 10 / 1);
	} else {
		buffer[0] = 0x00 & ~0x80;
		buffer[1] = 0x00 & ~0x80;
		buffer[2] = 0x00 & ~0x80;
		buffer[3] = 0x00 & ~0x80;
	}
	//memory copy
	memcpy(displayBuffer, buffer, 4);
}

/**
 * 设定浮点显示
 *
 * @param
 * @return
 */
void led8s595SetFloat(float value, uint8_t index) {
	// index: 0 or 1
	if (index > 1)
		return;
	// 设定4位
	setSegment4Float(&displayBuffer[4 * index], value);
}

/**
 * 设定显示buffer
 *
 * @param
 * @return
 */
void led8s595SetDisplayBuffer(uint8_t *buffer, uint8_t index) {
	// index: 0 or 1
	if (index > 1)
		return;
	//memory copy
	memcpy(&displayBuffer[4 * index], buffer, 4);
}

/**
 * 设定闪烁
 *
 * @param
 * @return
 */
void led8s595SetFlash(int8_t index) {

	if ((index >= 0) && (index < 8)) {

		flashSegment = 7 - index;
	} else {
		flashSegment = -1;
	}
}

/**
 * ledOut LED单次输出，共阴和共阳切换只需要在DIO(x)处更改
 *
 * @param
 * @return
 */
void ledOut(uint8_t X) {
	//
	for (uint8_t i = 8; i >= 1; i--) {
		if (X & 0x80)
			DIO(0);
		else
			DIO(1);
		X <<= 1;
		SCLK(0);
		__NOP();
		SCLK(1);
	}
}

/**
 * LED刷新任务（1ms调用）
 *
 * @param
 * @return
 */
void led8s595Task1ms(void) {
	// 数码管管位
	static uint8_t digit;
	// 毫秒数，用于闪烁计时
	static uint16_t ms;

	if (flashSegment != -1) {

		ms++;
		if (ms >= 1000) {

			ms = 0;
		}

	} else {

		ms = 0;
	}
	// 0-7位
	if (digit > 7)
		digit = 0;
	// 远端595，控制ABCDEF DP
	uint8_t X = displayBuffer[digit];

	// 如果显示位为闪烁位，判断闪烁
	if (digit == flashSegment) {

		if (ms >= 500) {

			X = 0xFF;
		} else {
			if (X == 0xFF) {

				X = 0xF7;
			}
		}
	}
	ledOut(X);
	// 近端595，控制segment
	ledOut(digitTable[digit]);
	// RCLK
	RCLK(0);
	__NOP();
	RCLK(1);
	digit++;
}

